package net.gazhi.delonix.rbac.web;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.autoconfigure.web.BasicErrorController;
import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpStatus;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import net.gazhi.delonix.core.annonation.ApiAction;
import net.gazhi.delonix.core.annonation.ApiLevel;
import net.gazhi.delonix.core.util.IPUtils;
import net.gazhi.delonix.core.web.ErrorPages;
import net.gazhi.delonix.rbac.entity.Action;
import net.gazhi.delonix.rbac.entity.LoginUser;
import net.gazhi.delonix.rbac.service.TicketService;
import net.gazhi.delonix.rbac.thread.RBACThreadContext;

/**
 * Handler方法鉴权拦截器处理器<br>
 * 在调用方法之前，判断是否有调用该方法的权限
 * 
 * @author jeffreylin@gazhi.net
 *
 */
public class ActionAuthorizationInterceptor extends HandlerInterceptorAdapter {

	private static Log logger = LogFactory.getLog(ActionAuthorizationInterceptor.class);

	@Autowired
	private TicketService ticketService;

	@Override
	public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

		// 非 HandlerMethod 不处理，例如 websocket 的情况为：WebSocketHttpRequestHandler
		if (!(handler instanceof HandlerMethod)) {
			return true;
		}

		HandlerMethod hm = (HandlerMethod) handler;

		if (hm.getBeanType().equals(BasicErrorController.class)) {
			return true;
		}

		// 基本变量定义
		String actionPath = request.getServletPath();
		int actionId = Action.generateActionId(hm);
		LoginUser loginUser = this.ticketService.checkAndCreateLoginUser(request);
		loginUser = loginUser == null ? LoginUser.guest() : loginUser;
		loginUser.setCurrentActionId(actionId);
		loginUser.setCurrentActionPath(actionPath);

		// 加入线程变量
		RBACThreadContext.setLoginUser(loginUser);
		request.setAttribute(LoginUser.LOGIN_USER_ATTRIBUTE, loginUser);

		// 检查是否有 ApiAction 注解（每个 controller 方法都要求有 ApiAction 注解）
		ApiAction apiAction = AnnotationUtils.getAnnotation(hm.getMethod(), ApiAction.class);
		if (apiAction == null) {
			throw new RuntimeException("Action annonation not found: @ApiAction, " + hm.toString());
		}

		// 开放的接口，直接放行
		if (apiAction.level().equals(ApiLevel.PUBLIC)) {
			return true;
		}

		// loginUser 为访客，说明 access_token 无效
		if (loginUser.isGuest()) {
			logger.info("Unauthorized request: " + actionPath + " from ip " + IPUtils.format(RBACThreadContext.getClientIp()));
			response.setStatus(HttpStatus.UNAUTHORIZED.value());
			request.getRequestDispatcher(ErrorPages.UNAUTHORIZED_401.getPath()).forward(request, response);
			return false;
		}

		// 登录用户默认就有的权限
		if (apiAction.level().equals(ApiLevel.USER_DEFAULT)) {
			return true;
		}

		// 超级管理员，直接放行
		if (loginUser.isSuperAdmin()) {
			return true;
		}

		// 拒绝非管理员请求超级权限
		if (apiAction.level().equals(ApiLevel.SUPER_ADMIN)) {
			// 拒绝访问
			logger.warn("Forbidden request: " + actionPath + " from ip " + IPUtils.format(RBACThreadContext.getClientIp()));
			response.setStatus(HttpStatus.FORBIDDEN.value());
			request.getRequestDispatcher(ErrorPages.FORBIDDEN_403.getPath()).forward(request, response);
			return false;
		}

		// 验证用户的功能点权限
		if (!loginUser.hasAuth(actionPath)) {
			// 拒绝访问
			logger.warn("Forbidden request: " + actionPath + " from ip " + IPUtils.format(RBACThreadContext.getClientIp()));
			response.setStatus(HttpStatus.FORBIDDEN.value());
			request.getRequestDispatcher(ErrorPages.FORBIDDEN_403.getPath()).forward(request, response);
			return false;
		}
		return true;
	}

}